﻿// Copyright 2009 Mike Geise
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

namespace Falcon.Core.Services
{
	using System.Globalization;
	using System.IO;
	using System.Net.Mail;
	using System.Threading;
	using System.Web.Mvc;
	using Falcon.Core.Domain.Services;
	using Falcon.Core.Email;
	using Falcon.Core.Extensions;
	using Spark;

	public class SmtpEmailService : IEmailService
	{
		/// <summary>
		/// Sends the email using the specified model.
		/// </summary>
		/// <param name="model">The model.</param>
		public void Send(object model)
		{
			var email = model as IEmail;

			if (email == null)
			{
				return;
			}

			Send(email.Template, email.To, email.From, email.Subject, email.Build());
		}

		/// <summary>
		/// Send the specified email
		/// </summary>
		/// <param name="to">The recipient.</param>
		/// <param name="from">The sender.</param>
		/// <param name="subject">The subject.</param>
		/// <param name="body">The body.</param>
		public void Send(string to, string from, string subject, string body)
		{
			ThreadPool.QueueUserWorkItem(state => Process(to, from, subject, body));
		}

		/// <summary>
		/// Send the specified email
		/// </summary>
		/// <param name="template">The template.</param>
		/// <param name="to">The recipient.</param>
		/// <param name="from">The sender.</param>
		/// <param name="subject">The subject.</param>
		/// <param name="model">The model.</param>
		public void Send(string template, string to, string from, string subject, object model)
		{
			var settings = new SparkSettings()
				.SetPageBaseType(typeof(TemplateBase))
				.SetDebug(false)
				.SetAutomaticEncoding(false);

			var engine = new SparkViewEngine(settings);
			var descriptor = new SparkViewDescriptor().AddTemplate("mail/{0}.spark".Fmt(template));
			var view = (TemplateBase)engine.CreateInstance(descriptor);

			TextWriter output;

			try
			{
				view.ViewData = new ViewDataDictionary(model);

				using (output = new StringWriter(CultureInfo.InvariantCulture))
				{
					view.RenderView(output);
				}
			}
			finally
			{
				engine.ReleaseInstance(view);
			}

			ThreadPool.QueueUserWorkItem(state => Process(to, from, subject, output.ToString()));
		}

		/// <summary>
		/// Processes the specified email.
		/// </summary>
		/// <param name="to">To.</param>
		/// <param name="from">From.</param>
		/// <param name="subject">The subject.</param>
		/// <param name="body">The body.</param>
		private static void Process(string to, string from, string subject, string body)
		{
			using (var message = new MailMessage())
			{
				message.To.Add(new MailAddress(to));
				message.From = new MailAddress(from);

				message.Subject = subject;
				message.Body = body;
				message.IsBodyHtml = false;

				var client = new SmtpClient();

				client.Send(message);
			}
		}
	}

	public abstract class TemplateBase : AbstractSparkView
	{
		/// <summary>
		/// Gets or sets the view data.
		/// </summary>
		/// <value>The view data.</value>
		public ViewDataDictionary ViewData
		{
			get;
			set;
		}

		/// <summary>
		/// Evals the specified expression.
		/// </summary>
		/// <param name="expression">The expression.</param>
		/// <returns></returns>
		public object Eval(string expression)
		{
			return ViewData.Eval(expression);
		}

		/// <summary>
		/// Evals the specified expression.
		/// </summary>
		/// <param name="expression">The expression.</param>
		/// <param name="format">The format.</param>
		/// <returns></returns>
		public string Eval(string expression, string format)
		{
			return ViewData.Eval(expression, format);
		}
	}
}